/****************************************************************
** Product  :   HP Subscriber Network Application Policy
** Module   :   gr-manager
** Date: Apr 10, 2015               
** Author: Joey Yi
** (C) Copyright 2015, Hewlett-Packard Company, All Rights Reserved.
** This software is the proprietary information of HP, Inc.  
** Use is subject to license terms.
*****************************************************************
** Description:   
**  SSConfigUtils class provide a group of methods to process session server configurations. 
*       Import SS configuration into a LinkedList object which include some "Config" object;
*       Add/Update configuration items in a LinkedList<Config> object
*       Export SS configuration to a specified file  
*       Save SS configuration to a specified file
*       Get host session servers information and put into a Map<String, Map<String, List<String>>> object.
****************************************************************/
package com.hp.snap.gr.utils;


import com.hp.snap.gr.exception.GRMException;
import com.hp.snap.gr.model.TaskGroup;
import com.hp.snap.gr.model.TaskTypes;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;


/**
 * SSConfigUtils class provide a group of methods to process session server configurations. 
 *
 * <P>
 * Version: 3.3 <br>
 * Author: Joey Yi
 * Date: Apr 10, 2015
 * 
 * </P>
 *
 **/
public class SSConfigUtils {

    public static final String SNAP_GR_NODE = "[" + Constants.CFG_GRG_ROOT + "]";
    public static final String SNAP_GR_PREFIX = "[" + Constants.CFG_GRG_ROOT + "/";

    public static final int CONFIG_SERVER_FAILED = 100;

    private static Logger logger = LoggerFactory.getLogger(SSConfigUtils.class);
    private static LinkedList<Config> configList = new LinkedList<Config>();

    public static Map<String, String> readGlobalConfig(String configFile, Map<String, String> configMap) throws GRMException {

        if (!new File(configFile).exists()) {
            throw new GRMException("The GR global config is not existing: " + configFile);
        }

        BufferedReader rd = null;
        try {
            rd = new BufferedReader(new FileReader(configFile));
            String line;
            String node = "";
            String currentParam = "";
            int count = 1;
            while ((line = rd.readLine()) != null) {
                line = line.trim();

                if (isNode(line)) {
                    node = line;
                    count = 1;
                    configMap.put(node, null);
                    configList.add(new Config(node, null, null, true, node));
                }

                if (!isNode(line) && line.contains("=")) {
                    String orgParameter = line.substring(0, line.indexOf("=")).trim();
                    String parameter = orgParameter;
                    String value = line.substring(line.indexOf("=") + 1, line.length());

                    if (!currentParam.equals(parameter)) {
                        currentParam = parameter;
                        count = 1;
                    } else {
                        parameter = parameter + count;
                        count++;
                    }

                    String key = node + "/" + parameter;
                    if (configMap.containsKey(key)) {
                        key = key + count;
                    }
                    configMap.put(key, GRMConfigManager.decode(value));
                    configList.add(new Config(node, orgParameter, value, false, key));
                }
            }

            linkConfigParameter(configMap);

        } catch (Exception e) {
            configMap.clear();
            configList.clear();
            throw new GRMException("Read GR Global config failed '" + configFile + "'", e);
        } finally {
            if (rd != null) {
                try {
                    rd.close();
                } catch (IOException e) {
                    //
                }
            }
        }
        return configMap;
    }


    private static void linkConfigParameter(Map<String, String> configMap) {
        for (String key : configMap.keySet()) {
            String value = configMap.get(key);
            if (value != null && value.startsWith("@/")) {
                String linkedParamPath = value.replaceFirst("@/", "[/");
                int index = value.lastIndexOf("/");
                String node = linkedParamPath.substring(0, index) + "]";
                String param = linkedParamPath.substring(index, linkedParamPath.length());

                value = configMap.get(node + param);
                configMap.put(key, value);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> convertToScriptContext(Map<String, String> configMap) throws GRMException {
        if (logger.isTraceEnabled()) {
            logger.trace("Starting to convert the global config for script context...");
        }

        Map<String, Object> globalConfigMap = new LinkedHashMap<String, Object>();

        String currentSiteName = GRMConfigManager.getSiteName();
        String remoteSiteName = null;

        String masterSlave = configMap.get(SNAP_GR_NODE + "/" + Constants.CFG_GRG_MASTER_SLAVE_RELATION);
        String[] masterSalvePairs = masterSlave.split("\\|");

        for (String masterSalvePair : masterSalvePairs) {
            String[] sites = masterSalvePair.split(":");
            if (sites.length != 2) {
                throw new GRMException("Site pair for master-slave mapping configuration error!");
            }

            String master = sites[0].trim();
            String slave = sites[1].trim();

            if (StringUtils.isEmpty(master) || StringUtils.isEmpty(slave)) {
                throw new GRMException("Site pair for master-slave mapping configuration error!");
            }

            if (currentSiteName.equals(master)) {
                remoteSiteName = slave;
                break;
            }

            if (currentSiteName.equals(slave)) {
                remoteSiteName = master;
                break;
            }
        }


        String currentParam = "";
        int count = 1;
        for (String key : configMap.keySet()) {

            if ((!key.startsWith(SNAP_GR_NODE) && !key.startsWith(SNAP_GR_PREFIX)) || key.endsWith("]")) {
                continue;
            }

            Map<String, Object> lastLayerMap;
            String paramName;

            if (key.startsWith(SNAP_GR_NODE) && !key.equals(SNAP_GR_NODE)) {
                int index = key.indexOf("]");
                paramName = key.substring(index + 2, key.length());
                lastLayerMap = getLastLayerMap(globalConfigMap, null);
            } else {
                String grSubNode = key.replace(SNAP_GR_PREFIX, "");
                String[] subNodes = grSubNode.split("/");
                lastLayerMap = getLastLayerMap(globalConfigMap, subNodes);
                int index = key.lastIndexOf("/");
                paramName = key.substring(index + 1, key.length());
            }

            String realParamName = paramName;
            boolean isArray = false;
            if (!"".equals(currentParam) && (currentParam + count).equals(paramName)) {
                int suffixIndex = paramName.lastIndexOf(String.valueOf(count));
                realParamName = paramName.substring(0, suffixIndex);
                currentParam = realParamName;
                count++;
                isArray = true;
            } else {
                currentParam = paramName;
                count = 1;
            }

            Object object = configMap.get(key);

            if (isArray) {
                List<Object> storedArray = new LinkedList<Object>();
                if (lastLayerMap.containsKey(realParamName)) {
                    Object storedObject = lastLayerMap.get(realParamName);
                    if (!(storedObject instanceof List)) {
                        storedArray.add(storedObject);
                    } else {
                        storedArray = (List<Object>) storedObject;
                    }
                }
                storedArray.add(object);
                lastLayerMap.put(realParamName, storedArray);
            } else {
                lastLayerMap.put(realParamName, object);
            }
        }

        Map<String, Object> currentSiteMap = (Map<String, Object>) globalConfigMap.get(currentSiteName);
        Map<String, Object> remoteSiteMap = (Map<String, Object>) globalConfigMap.get(remoteSiteName);

        globalConfigMap.put(Constants.CURRENT_DEPLOY, currentSiteMap);
        globalConfigMap.put(Constants.REMOTE_DEPLOY, remoteSiteMap);

        linkHostNode(globalConfigMap, masterSalvePairs);

        if (logger.isTraceEnabled()) {
            logger.trace("Finished to convert global config.");
        }

        return globalConfigMap;
    }

    @SuppressWarnings("unchecked")
    private static void linkHostNode(Map<String, Object> globalConfigMap, String[] masterSalvePairs) throws GRMException {
        Set<String> uniqueSites = new HashSet<String>();
        for (String masterSalvePair : masterSalvePairs) {
            String[] sites = masterSalvePair.split(":");
            if (sites.length != 2) {
                throw new GRMException("Site pair for master-slave mapping configuration error!");
            }

            String master = sites[0];
            String slave = sites[1];

            uniqueSites.add(master);
            uniqueSites.add(slave);
        }

        for (String uniqueSite : uniqueSites) {
            Map<String, Object> siteMap = (Map<String, Object>) globalConfigMap.get(uniqueSite);
            if (siteMap == null) {
                continue;
            }

            // link GR Host
            String grHostId = (String) siteMap.get(Constants.CFG_GRG_SITE_GR_HOST);
            if (StringUtils.isEmpty(grHostId)) {
                throw new GRMException("The GR Host Id should be configured!");
            }
            Map<String, Object> grHostMap = getLastLayerMap(globalConfigMap, new String[]{uniqueSite, Constants.CFG_GRG_HOST, grHostId});

            if (grHostMap == null || grHostMap.isEmpty()) {
                throw new GRMException("The host id [" + grHostId + "] is not configured!");
            }

            siteMap.put(Constants.CFG_GRG_SITE_GR_HOST, grHostMap);


            // link VEM Host
            Map<String, Object> voltdbMap = (Map<String, Object>) siteMap.get(Constants.CFG_GRG_VOLTDB);
            if (voltdbMap != null) {
                String vemHostId = (String) voltdbMap.get(Constants.CFG_GRG_VEM_HOST);
                if (StringUtils.isEmpty(vemHostId)) {
                    throw new GRMException("The VEM Host Id should be configured!");
                }
                Map<String, Object> vemHostMap = getLastLayerMap(globalConfigMap, new String[]{uniqueSite, Constants.CFG_GRG_HOST, vemHostId});

                if (vemHostMap == null || vemHostMap.isEmpty()) {
                    throw new GRMException("The host id [" + vemHostId + "] is not configured!");
                }

                voltdbMap.put(Constants.CFG_GRG_VEM_HOST, vemHostMap);
            }


            // link site SSH user to host SSH user
            String siteSSHUser = (String) siteMap.get(Constants.CFG_GRG_SITE_SSH_USER);
            Map<String, Object> hostParentMap = getLastLayerMap(globalConfigMap, new String[]{uniqueSite, Constants.CFG_GRG_HOST});
            for (String hostId : hostParentMap.keySet()) {
                Map<String, Object> hostMap = (Map<String, Object>) hostParentMap.get(hostId);
                String hostSSHUser = (String) hostMap.get(Constants.CFG_GRG_HOST_SSH_USER);
                if (StringUtils.isEmpty(hostSSHUser)) {
                    hostMap.put(Constants.CFG_GRG_HOST_SSH_USER, siteSSHUser);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static Map<String, Object> getLastLayerMap(Map<String, Object> globalConfigMap, String[] keyLevels) {
        Map<String, Object> lastLevelMap = globalConfigMap;

        if (keyLevels == null || keyLevels.length == 0) {
            return lastLevelMap;
        }

        for (String keyLevel : keyLevels) {
            boolean lastLevel = false;
            if (keyLevel.endsWith("]")) {
                lastLevel = true;
                keyLevel = keyLevel.replace("]", "");
            }

            if (!lastLevelMap.containsKey(keyLevel)) {
                lastLevelMap.put(keyLevel, new LinkedHashMap<String, Object>());
            }

            lastLevelMap = (Map<String, Object>) lastLevelMap.get(keyLevel);

            if (lastLevel) {
                break;
            }
        }

        return lastLevelMap;
    }


    public static boolean isNode(String line) {
        return line.startsWith("[") && line.endsWith("]");
    }

    public static void readSSConfig(String configFile) {
        configList.clear();
        if (!new File(configFile).exists()) {
            logger.error("The SS config not existing: " + configFile);
        }

        BufferedReader rd = null;
        try {
            rd = new BufferedReader(new FileReader(configFile));
            String line;
            String node = "";
            String currentParam = "";
            int count = 1;
            while ((line = rd.readLine()) != null) {
                line = line.trim();

                if (isNode(line)) {
                    node = line;
                    count = 1;
                    configList.add(new Config(node, null, null, true, node));
                }

                if (!isNode(line) && line.contains("=")) {
                    String orgParameter = line.substring(0, line.indexOf("=")).trim();
                    String parameter = orgParameter;
                    String value = line.substring(line.indexOf("=") + 1, line.length());

                    if (!currentParam.equals(parameter)) {
                        currentParam = parameter;
                        count = 1;
                    } else {
                        parameter = parameter + count;
                        count++;
                    }

                    String key = node + "/" + parameter;
                    configList.add(new Config(node, orgParameter, value, false, key));
                }
            }

        } catch (Exception e) {
            logger.error("Read GR Global config failed '" + configFile + "', error: " + e.getMessage());
            configList.clear();
        } finally {
            if (rd != null) {
                try {
                    rd.close();
                } catch (IOException e) {
                    //
                }
            }
        }
    }

    public static void replaceConfig(String node, String param, String oldValue, String value) {
        String nodeParam = "[" + node + "]/" + param;
        for (int i = configList.size() - 1; i > 0; i--) {
            Config config = configList.get(i);

            if (config.getPath().equals(nodeParam)) {
                config.setValue(value);
                if (logger.isTraceEnabled()) {
                    logger.trace("Replace config item '" + nodeParam + "', from: " + oldValue + ", to: " + value);
                }
                break;
            }
        }
    }

    public static void addConfig(String node, String name, String value, boolean isNode, int arrayIndex) {
        node = "[" + node + "]";

        int index = configList.size();
        for (int i = configList.size() - 1; i > 0; i--) {
            Config config = configList.get(i);
            if (isNode) {
                if (!config.getNodeName().contains("/RTP/Plan]")) {
                    index = i + 1;
                    break;
                }
            } else {
                if (config.getNodeName().equals(node)) {
                    if (arrayIndex != -1) {
                        if (config.isNode()) {
                            // the array index is start from '1'
                            index = i + arrayIndex;
                            break;
                        }
                    } else {
                        index = i + 1;
                        break;
                    }
                }
            }
        }

        Config newConfig = new Config(node, name, value, isNode, node + "/" + name);
        configList.add(index, newConfig);
    }

    public static String getUpdatedSSConfig() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < configList.size(); i++) {
            Config config = configList.get(i);
            if (config.isNode()) {
                sb.append("\n\n");
                sb.append(config.getNodeName()).append("\n\n");
            } else {
                sb.append(config.getName()).append("=").append(config.getValue()).append("\n");

            }
        }
        return sb.toString();
    }

    public static void saveSSConfig(String filePath) throws GRMException {
        String contents = getUpdatedSSConfig();
        boolean result = FileUtils.writeFile(contents, filePath);
        if (!result) {
            throw new GRMException("Cannot save config: " + filePath);
        }
    }


    public static void exportGRGlobalConfig(TaskTypes taskType) throws GRMException {
          //Currently, the SGT is invoked by the SGM, So, skip the exportGRGlobalConfig in the SGT operation.
//        if(!taskType.toString().startsWith(TaskGroup.SGM.toString()) && !taskType.toString().startsWith(TaskGroup.SGT.toString())){
        if(!taskType.toString().startsWith(TaskGroup.SGM.toString())){
            if (logger.isDebugEnabled()) {
                logger.debug("Skip export GRGlobalConfig.");
            }
            return;
        }
        
        if (logger.isDebugEnabled()) {
            logger.debug("Starting to export GR global config...");
        }

        String globalConfigDest = GRMConfigManager.getGRGlobalConfigPath();

        File grGlobalFile = new File(globalConfigDest);
        File bakGlobalFile = new File(globalConfigDest + ".bak");

        boolean backupSuccess = false;
        if (grGlobalFile.isFile()) {
            FileUtils.deleteFile(bakGlobalFile);
            backupSuccess = grGlobalFile.renameTo(bakGlobalFile);
            if (!backupSuccess) {
                logger.warn("Failed to backup the previous exported GR global config.");
            }
        }

        int exitStatus = SSConfigUtils.exportConfigServer("/SNAP", globalConfigDest);
        if (exitStatus != 0) {
            if (exitStatus == CONFIG_SERVER_FAILED && backupSuccess && isIgnoreConfigServerFailure(taskType)) {

                // the command would generate an empty file and it would make the rename failed
                FileUtils.deleteFile(grGlobalFile);

                boolean recoverSuccess = bakGlobalFile.renameTo(new File(globalConfigDest));

                if (!recoverSuccess) {
                    throw new GRMException("Cannot contact the configuration server and cannot continue!");
                }
            } else {
                throw new GRMException("Failed to export the GR global config.");
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Successful to export the global config to: {}", globalConfigDest);
            }
        }
    }

    public static boolean isIgnoreConfigServerFailure(TaskTypes taskType) {
        return true;
    }

    public static void exportGRConfig() throws GRMException {
        if (logger.isDebugEnabled()) {
            logger.debug("Starting to export GR config for sync...");
        }

        String configDest = GRMConfigManager.getGRConfigPath();
        int exitStatus = SSConfigUtils.exportConfigServer("/SNAP/GR", configDest);
        if (exitStatus != 0) {
            throw new GRMException("Failed to export config: /SNAP/GR");
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Successful to export the GR config to: {}", configDest);
        }
    }


    public static int exportConfigServer(String path, String dest) throws GRMException {
        if (isInTimeWindow(dest)) {
            return 0;
        }

        boolean isSecure = GRMConfigManager.isSecure();
        String eIUMHome = GRMConfigManager.getEIUMHome();
        String username = GRMConfigManager.getUsername();
        String password = GRMConfigManager.getPassword();


        String eiumBin = eIUMHome + File.separator + "bin";

        String userPwd = "";
        if (isSecure) {
            userPwd = " -login " + username + " -pw " + password;
        }

        String backupCmd = eiumBin + File.separator + "saveconfig" + userPwd + " -p " + path + " -f " + dest;

        FileUtils.createFile(dest);

        return accessConfigServer(backupCmd, true, password);
    }

    public static int accessConfigServer(String cmd, boolean couldHavePwd, String password) {
        CmdResult cmdResult = new CmdResult();
        int exitstatus;
        try {
            exitstatus = CmdUtils.runCmdRtnStatus(cmd, couldHavePwd, password, cmdResult);
        } catch (Exception e) {
            logger.error("Cannot run the save config command", e);
            return 1;
        }

        if (exitstatus != 0) {
            boolean configServerFailed = cmdResult.getStderr().toString().contains("Cannot contact the configuration server");
            if (configServerFailed) {
                Constants.CONSOLE.info("Cannot contact the configuration server!");
                return CONFIG_SERVER_FAILED;
            }
        }
        return exitstatus;
    }

    private static boolean isInTimeWindow(String ssConfig) {
        File ssConfigFile = new File(ssConfig);
        if (!ssConfigFile.exists()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Target file not existing: {}", ssConfig);
            }

            return false;
        }

        long lastModifiedTime = ssConfigFile.lastModified();
        long currentTime = System.currentTimeMillis();

        String twConfig = GRMConfigManager.getLocalConfigValue(Constants.CFG_GRL_SS_CONFIG_EXPORT_TW, Constants.DEFAULT_SS_CONFIG_EXPORT_TW);
        long exportTW = Long.parseLong(twConfig) * 1000;

        if (exportTW <= 0 || currentTime - lastModifiedTime > exportTW) {
            if (logger.isDebugEnabled()) {
                logger.debug("Target file existing, but NOT in time window: targetFile={}, currentTime={}, exportTime={}, timeWindow={}",
                        new Object[]{ssConfig, currentTime, lastModifiedTime, exportTW});
            }
            return false;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Target file existing and in time window:targetFile={}, currentTime={}, exportTime={}, timeWindow={}",
                    new Object[]{ssConfig, currentTime, lastModifiedTime, exportTW});
        }

        return true;
    }

    public static void importConfigServer(String path) throws GRMException {
        boolean isSecure = GRMConfigManager.isSecure();
        String eIUMHome = GRMConfigManager.getEIUMHome();
        String username = GRMConfigManager.getUsername();
        String password = GRMConfigManager.getPassword();


        String eiumBin = eIUMHome + File.separator + "bin/";

        String userPwd = "";
        if (isSecure) {
            userPwd = " -login " + username + " -pw " + password;
        }

        // load the updated config and overwrite the old one
        String loadCmd = eiumBin + "loadconfig" + userPwd + " -f " + path + " -merge";
        int exitStatus = accessConfigServer(loadCmd, true, password);

        if (exitStatus != 0) {
            if (exitStatus == CONFIG_SERVER_FAILED) {
                logger.warn("Cannot load the session server config '" + path + "' due to cannot contact configuration server.");
            } else {
                throw new GRMException("Load session server config '" + path + "' failed!");
            }
        }
    }

    public static Map<String, Map<String, List<String>>> getHostSessionServers() throws GRMException {
        String configTarget = Constants.USER_GR_TEMP + File.separator + "All.config";

        exportConfigServer("/", configTarget);

        String[] ssTypeConfig = Utils.getMultiConfigurationValue(Constants.CFG_GRG_SESSION_SERVER, Constants.CFG_GRG_SESSION_SERVER_TYPES);

        if (logger.isTraceEnabled()) {
            logger.trace("ssTypeConfig is: {}", ssTypeConfig);
        }
        
        Map<String, List<String>> ssTypePattern = new LinkedHashMap<String, List<String>>();

        if (ssTypeConfig != null) {
            for (String ssType : ssTypeConfig) {
                String[] typeNames = ssType.split(",");
                if (typeNames.length != 2) {
                    throw new GRMException("The Session Server Type config error: " + ssType);
                }
                String type = typeNames[0];
                String[] names = typeNames[1].split("\\|");
                if (names.length == 0) {
                    throw new GRMException("The Session Server Type config error: " + ssType);
                }

                ssTypePattern.put(type, Arrays.asList(names));
            }
        }

        Map<String, Map<String, List<String>>> ssTypeHostNodes = new LinkedHashMap<String, Map<String, List<String>>>();

        BufferedReader rd = null;
        try {
            rd = new BufferedReader(new FileReader(configTarget));
            String line;
            while ((line = rd.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("[/deployment/") && line.endsWith("]")) {
                    String s = line.replaceFirst("\\[/", "").replace("]", "");
                    String[] array = s.split("/");
                    if (array.length == 3) {
                        String host = array[1];
                        String node = array[2];
                        String type = matchType(ssTypePattern, node);
                        if (type == null) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("Cannot find the matched SS type for: " + node);
                            }
                            continue;
                        }

                        Map<String, List<String>> hostNodes = ssTypeHostNodes.get(type);
                        if (hostNodes == null) {
                            hostNodes = new LinkedHashMap<String, List<String>>();
                            ssTypeHostNodes.put(type, hostNodes);
                        }

                        List<String> nodes = hostNodes.get(host);
                        if (nodes == null) {
                            nodes = new LinkedList<String>();
                            hostNodes.put(host, nodes);
                        }

                        nodes.add(node);
                    }
                }
            }
            if (logger.isTraceEnabled()) {
                logger.trace("construct the ssTypeHostNodes is: {}", ssTypeHostNodes);
            }
            ssTypeHostNodes = sortSessionServerTypes(ssTypeHostNodes);

        } catch (Exception e) {
            throw new GRMException("Read session server config failed '" + configTarget + "'", e);
        } finally {
            if (rd != null) {
                try {
                    rd.close();
                } catch (IOException e) {
                    //
                }
            }
        }


        return ssTypeHostNodes;
    }

    private static Map<String, Map<String, List<String>>> sortSessionServerTypes(Map<String, Map<String, List<String>>> ssTypeHostNodes) {
        String ssTypeStartSeqConfig = Utils.getConfigurationValue(Constants.CFG_GRG_SESSION_SERVER, Constants.CFG_GRG_SESSION_SERVER_START_SEQUENCE);

        if (StringUtils.isEmpty(ssTypeStartSeqConfig)) {
            return ssTypeHostNodes;
        }

        Map<String, Map<String, List<String>>> sortedSSTypeHostNodes = new LinkedHashMap<String, Map<String, List<String>>>();

        String[] types = ssTypeStartSeqConfig.split(",");
        if (types.length != 0) {
            for (String type : types) {
                type = type.trim();
                sortedSSTypeHostNodes.put(type, ssTypeHostNodes.get(type));
            }
        }

        for (String key : ssTypeHostNodes.keySet()) {
            if (!sortedSSTypeHostNodes.containsKey(key)) {
                sortedSSTypeHostNodes.put(key, ssTypeHostNodes.get(key));
            }
        }

        return sortedSSTypeHostNodes;
    }

    private static String matchType(Map<String, List<String>> ssTypePattern, String sessionServer) {
        for (String type : ssTypePattern.keySet()) {
            List<String> patterns = ssTypePattern.get(type);
            for (String pattern : patterns) {
                boolean result;
                if (pattern.endsWith("*")) {
                    pattern = pattern.substring(0, pattern.length() - 1);
                    result = sessionServer.startsWith(pattern);
                } else {
                    result = sessionServer.equals(pattern);
                }
                if (result) {
                    return type;
                }
            }
        }

        return null;
    }

    public static class Config {
        private String nodeName;
        private String name;
        private String value;
        private boolean isNode;
        private String path;

        public Config(String nodeName, String name, String value, boolean node, String path) {
            this.nodeName = nodeName;
            this.name = name;
            this.value = value;
            this.isNode = node;
            this.path = path;
        }

        public String getNodeName() {
            return nodeName;
        }

        public void setNodeName(String nodeName) {
            this.nodeName = nodeName;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public boolean isNode() {
            return isNode;
        }

        public void setNode(boolean node) {
            isNode = node;
        }

        public String getNodeParam() {
            if (name == null) {
                return nodeName;
            } else {
                return nodeName + "/" + name;
            }
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Config{");
            sb.append("nodeName='").append(nodeName).append('\'');
            sb.append(", name='").append(name).append('\'');
            sb.append(", value='").append(value).append('\'');
            sb.append(", isNode=").append(isNode);
            sb.append(", path='").append(path).append('\'');
            sb.append('}');
            return sb.toString();
        }
    }

}
